package org.jenkinsci.plugins.p4scm;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import hudson.CopyOnWrite;
import hudson.EnvVars;
import hudson.Extension;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.EnvironmentSpecific;
import hudson.model.TaskListener;
import hudson.model.Descriptor;
import hudson.model.Descriptor.FormException;
import hudson.model.Node;
import hudson.slaves.NodeSpecific;
import hudson.tools.ToolProperty;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolInstallation;
import hudson.util.FormValidation;

public class P4ToolInstallation extends ToolInstallation implements
        NodeSpecific<P4ToolInstallation>, EnvironmentSpecific<P4ToolInstallation> {
    
    private static final Logger logger = LoggerFactory.getLogger(P4ToolInstallation.class);
    
    //TODO: why transient 
    static transient ArrayList<P4ToolInstallation> p4Tools = new ArrayList<P4ToolInstallation>();

    @DataBoundConstructor
    public P4ToolInstallation(String name, String home,
            List<? extends ToolProperty<?>> properties) {
        super(name, home, properties);
    }
    
    
    /**
     * Get the path to p4.exe
     * 
     * @return Path to p4.exe
     */
    public String getP4Exe() {
        return getHome();
    } 
    
    /**
     * Migrate old data into new tool installations if needed.
     */
    @Initializer(after=InitMilestone.JOB_LOADED)
    public static void onLoaded() {
        logger.info("Migrating old data into new tool installations after job loaded...");
        DescriptorImpl descriptor = P4ToolInstallation.getInstance();
        P4ToolInstallation[] installations = descriptor.getInstallations();

        //Allow only one migration round. Old "p4Exe" field is kept in job configuration until the job is saved.
        if (installations.length > 0) {
            return;
        }

        if (!p4Tools.isEmpty()) {
            descriptor.setInstallations(p4Tools.toArray(new P4ToolInstallation[p4Tools.size()]));
        }
    }
    
    /**
     * Migrate data from old job specific "p4Exe" field. Create a tool installation for each
     * individual path with the path as the tool name.
     * 
     * @param exe The path to p4 executable
     */
    public static synchronized void migrateOldData(String exe) {
        for (P4ToolInstallation tool : p4Tools) {
            //Tool installation already exists, Unix case
            if (File.separatorChar == '/' && tool.getName().equals(exe)) {
                return;
            }
            //Tool installation already exists, Windows case
            if (File.separatorChar != '/' && tool.getName().equalsIgnoreCase(exe)) {
                return;
            }
        }
        p4Tools.add(new P4ToolInstallation(exe, exe, Collections.<ToolProperty<?>>emptyList()));
    }
    
    public static DescriptorImpl getInstance() {
        return (DescriptorImpl) Jenkins.getInstance().getDescriptorOrDie(P4ToolInstallation.class);
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }


    @Override
    public P4ToolInstallation forEnvironment(EnvVars environment) {
        return new P4ToolInstallation(getName(), environment.expand(getHome()), Collections.<ToolProperty<?>>emptyList());
    }

    @Override
    public P4ToolInstallation forNode(Node node, TaskListener log)
            throws IOException, InterruptedException {
        return new P4ToolInstallation(getName(), translateFor(node, log), Collections.<ToolProperty<?>>emptyList());
    }
    
    @Extension
    public static class DescriptorImpl extends ToolDescriptor<P4ToolInstallation> {

        // TODO: why such variable need be set as volatile and add CopyOnWrite 
        @CopyOnWrite
        private volatile P4ToolInstallation[] installations = new P4ToolInstallation[0];

        public DescriptorImpl() {
            super();
            load();
        }

        @Override
        public String getDisplayName() {
            return "P4 SCM";
        }
        
        @Override
        public P4ToolInstallation[] getInstallations() {
            //return super.getInstallations();
            return this.installations;
        }

        @Override
        public void setInstallations(P4ToolInstallation... installations) {
            // super.setInstallations(installations);
            this.installations = installations;
            save();
        }

        @Override
        public boolean configure(StaplerRequest req, JSONObject json)
                throws FormException {
            logger.info("Configuring P4 Tool Installation ...");
            boolean superRet = super.configure(req, json);
            if(superRet) {
                save();
            } 
            return superRet;
        }

        /* (non-Javadoc)
         * @see hudson.tools.ToolDescriptor#doCheckHome(java.io.File)
         */
        @Override
        public FormValidation doCheckHome(@QueryParameter File value) {
            //return super.doCheckHome(value); // TODO: why do not use parent's doCheckHome
            logger.info("Checking P4 Exe Home ... ");
            
            Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
            String path = value.getPath();
            
            logger.info("The P4 Exe path is: {}", path);
            
            return FormValidation.validateExecutable(path);
        }
        
    }

}
